home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 18 / CU Amiga Magazine's Super CD-ROM 18 (1997)(EMAP Images)(GB)[!][issue 1998-01].iso / CUCD / Programming / ARexxGuide / Editors / ARx_Help.ed next >
Text File  |  1994-03-30  |  21KB  |  536 lines

  1. /** $VER: ARx_Help.ed 2.0a (28 Feb 1994,30 Mar 1994)
  2.  ** by Robin Evans
  3.  **
  4.  ** Display the ARexxGuide page for the word currently under the cursor.
  5.  ** Will call ARx_Setup.rexx if env: variables are not set.
  6.  **
  7.  ** Add the following line to the file S:Ed-Startup or whatever other file
  8.  ** you use to set Ed's default configuration.
  9.  **
  10.  **     sf 1 "rx \arx_help\"
  11.  **
  12.  **     This will hook ARexxGuide into the F1 key. Consult a manual for
  13.  **     other possible key assignments. The same command can also be issued
  14.  **     from within an Ed session to make a temporary assignment. Just type
  15.  **     in the command at a `*' prompt after pressing the <Esc> key. (Those
  16.  **     who have been reading Denny Atkin's DOS column in AmigaWorld have
  17.  **     learned that Ed has a far more comprehensive menu if the menu items
  18.  **     in the Ed-Startup file are deleted.)
  19.  **
  20.  **  Environmental variables:
  21.  **      env:AmigaGuide/path must include the directory where
  22.  **          arx_guide.xref is located.
  23.  **      env:ARexxGuide/AGCmd must hold the name of the command
  24.  **          (probably something like `sys:utilities/Multiview') used to
  25.  **          show launch amigaguide files.
  26.  **      env:ARexxGuide/ShowFullHelp must be set to 1 if the rexxarplib
  27.  **          information window is to be shown. rexxarplib.library is
  28.  **          required for that feature only.
  29.  **
  30.  ** See `Tutorials: Help key macros' ( HELPKEYMACRO ) in ARexxGuide for more
  31.  ** information about this macro.
  32.  **/
  33.  
  34. signal on syntax
  35. signal on break_c
  36. signal on halt
  37. signal on failure
  38.  
  39. options results
  40. parse arg LkUp
  41.  
  42.  
  43. call addlib('rexxsupport.library',0,-30)
  44. call addlib('amigaguide.library',-1,-30)
  45.  
  46. TRUE=1; FALSE=0
  47.  
  48.    /* Load the cross-ref file. Function returns quickly if the file is  **
  49.    ** already loaded, but -- like ADDLIB() it can return true even if   **
  50.    ** the file isn't available, so we check by looking for a known node.*/
  51. call DisplayStatus('ARx_Help: Checking .xref files...')
  52. do Attempt = 1
  53.     call LoadXref('ARx_Guide.xref')
  54.    if getxref('TRACEOPT1') = 10 then do   /* Leave below */
  55.       /* Did this in a loop just to be able to use signal for a no-load */
  56.       if Attempt = 2 then
  57.          signal XRefError
  58.       else if 'ARx_Setup.rexx'('HELP',,address()) ~= 0 then signal XRefError
  59.    end
  60.    else leave Attempt
  61. end
  62.  
  63. /* Add any extra files included in the env: variable.                */
  64. ExtraXRef = GetEnv('ARexxGuide/XRFiles')
  65. do i = 1 for words(ExtraXRef)
  66.       /* 'Call' because we don't care about errors here.             */
  67.    call loadxref(word(ExtraXRef, i))
  68. end
  69.  
  70. call DisplayStatus('ARexxGuide online help system...')
  71.  
  72.    /* It isn't used in the setup provided here, but it's possible to    **
  73.    ** just pass a word as an argument to this routine and get a lookup  **
  74.    ** on that.                                                          */
  75. if LkUp = '' then do
  76.       /* Get the current line and position. Editor's commands are all   **
  77.       ** in subroutines and not in the main code.                       */
  78.    HLine = GetCLine()
  79.    CurPos = GetCurPos()
  80.       /* Show the index requester if it's a blank line */
  81.    if HLine = '' | verify(HLine, ' ;') = 0 then do
  82.       call ShowInfo(,, CurPos, Kwd)
  83.       signal CleanExit
  84.    end
  85.  
  86.       /* Cut a single clause out of preceding clauses                   */
  87.    SemPos = lastpos(';', HLine, CurPos) + 1
  88.       /* This will break if a semicolon is used within a string         **
  89.       ** but checking for that seems like overkill.                     */
  90.    if SemPos > 1 then do
  91.       if SemPos < CurPos + 1 then do
  92.          parse var HLine =SemPos HLine ';'
  93.          CurPos = CurPos - SemPos + 1
  94.       end
  95.    end
  96.       /* Cut it out of following clauses                                 */
  97.    else
  98.       parse var HLine HLine ';'
  99.  
  100.    /* The current keyword is used within MChar select clause             */
  101.    Kwd = upper(word(HLine, 1))
  102.  
  103.                                               /* Hex characters: SpTbRt    */
  104.       /* Check for special characters -- operators, quotation marks, etc */
  105.    MChar = pos(substr(HLine,CurPos,1), '+-/*%|&^><=,;:~)("''?!.'||'20090a'x)
  106.    select
  107.       when MChar = 0 then do     /* It isn't one of the chars */
  108.          /* Look for whole word if it isn't a special character */
  109.          LkUp = GetCWord(HLine, CurPos)
  110.       end
  111.       when MChar <= 5 then
  112.          select
  113.             when MChar <= 2 then       /* + or - */
  114.                if Kwd = 'PARSE' then
  115.                   LkUp = 'PARSETMP5'
  116.                else
  117.                   LkUp = 'ARITHMETIC'
  118.             when MChar <= 4 then do    /* / or * */
  119.                   /* This picks up the three characters surrounding a '*' or '/' */
  120.                ComChar = substr(HLine, max(1, CurPos - 1), 3)
  121.                if pos('/*', ComChar) + pos('*/', ComChar) > 0 then
  122.                   LkUp = 'COMMENT'
  123.                else
  124.                   LkUp = 'ARITHMETIC'
  125.             end
  126.             otherwise
  127.                LkUp = 'ARITHMETIC'        /* % */
  128.          end
  129.       when MChar <= 13 then
  130.          select
  131.             when MChar <= 8 then do    /* | or & or ^ */
  132.                   /* This picks up the three characters surrounding token */
  133.                ComChar = substr(HLine, max(1, CurPos - 1), 3)
  134.                if pos('||', ComChar) > 0 then
  135.                   LkUp = 'CONCATENATION'
  136.                else
  137.                   LkUp = 'LOGICAL'
  138.             end
  139.             when MChar <= 10 then    /* < or > */
  140.                LkUp = 'COMPARISON'
  141.             when MChar = 11 then    /* = */
  142.                if Kwd = 'PARSE' then
  143.                   LkUp = 'PARSETMP5'
  144.                else if symbol(strip(left(HLine, max(1, CurPos-1)))) == 'BAD' then
  145.                   LkUp = 'COMPARISON'
  146.                else
  147.                   LkUp = 'ASSIGNMENT'
  148.             when MChar = 12 then    /* , */
  149.                LkUp = 'COMMA'
  150.             otherwise               /* ; */
  151.                LkUp = 'SEMICOLON'
  152.          end
  153.       when MChar <= 19 then
  154.          select
  155.             when MChar = 14 then    /* : */
  156.                LkUp = 'LABEL'
  157.             when MChar = 15 then do /* ~ */
  158.                   /* is the next character a comparison operator? */
  159.                if pos(substr(HLine, CurPos , 2), '~=~>~<') > 0 then
  160.                   LkUp = 'COMPARISON'
  161.                else
  162.                   LkUp = 'LOGICAL'
  163.             end
  164.             when MChar <=17 then    /* ) or ( */
  165.                if Kwd = 'PARSE' then
  166.                   LkUp = 'PARSETMP5'
  167.                else
  168.                   LkUp = 'PAREN'
  169.             otherwise               /* " or ' */
  170.                LkUp = 'STRING'
  171.          end
  172.       otherwise
  173.          select
  174.             when MChar = 20 then    /* ? */
  175.                if Kwd = 'TRACE' then
  176.                   LkUp = 'TRACEOPT1'
  177.                else
  178.                   LkUp = '?'
  179.             when MChar = 21 then    /* ! */
  180.                if Kwd = 'TRACE' then
  181.                   LkUp = 'TRACEOPT2'
  182.                else
  183.                   LkUp = '!'
  184.             when MChar = 22 then do   /* . -- dot  */
  185.                   /* Get the surrounding ARexx token */
  186.                StPos = verify(' 'HLine, '+-|/&=~\><^,;:()"'' ', 'M', lastpos(' ', ' 'HLine, CurPos))
  187.                Token = substr(HLine, StPos, verify(HLine' ', '+-|/&=~\><^,;:()"'' ', M, CurPos) - StPos)
  188.                select
  189.                   when Token = '.' then
  190.                      if Kwd = 'PARSE' then
  191.                         LkUp = 'PARSETMP2'
  192.                      else
  193.                         LkUp = Token
  194.                   when datatype(Token, 'N') then
  195.                      LkUp = 'NUMBER'
  196.                   when datatype(Token, 'S') then
  197.                      if datatype(left(Token,1),'M') | verify(Token, '!@#$', 'M') = 1
  198.                         then LkUp = 'COMPVAR'
  199.                   otherwise
  200.                      LkUp = Token
  201.                end
  202.             end
  203.             otherwise               /* At a space or EOL */
  204.                if CurPos >= wordindex(HLine, 1) then
  205.                   LkUp = GetCWord(HLine, CurPos)
  206.                else do
  207.                   call ShowInfo(HLine,, CurPos, Kwd)
  208.                   signal CleanExit
  209.                end
  210.          end
  211.    end
  212.  
  213.  
  214.    /* Check for special words like `command' in `address command'       **
  215.    ** and most of the subkeywords in instructions like DO.              */
  216.    if word(HLine, 1) ~= LkUp then do CheckOpt = 1 for 1  /* Let's us leave */
  217.       if GetXRef(LkUp) = 10 | find('ARG PULL UPPER COMMAND', upper(LkUp)) > 0 then do
  218.          if word(GetXRef(Kwd),3) = 2 then select
  219.             when Kwd = 'DO' then do
  220.                SpecWord = find('UNTIL WHILE TO FOR BY FOREVER ', upper(LkUp))
  221.                if SpecWord > 0 then
  222.                   LkUp = word('DOOPT4 DOOPT4 DOOPT2 DOOPT3 DOOPT2 DOOPT5', SpecWord)
  223.             end
  224.             when Kwd = 'PARSE' then do
  225.                SpecWord = find('ARG PULL EXTERNAL NUMERIC SOURCE VERSION VALUE VAR WITH', upper(LkUp))
  226.                if SpecWord > 0 then do
  227.                   LkUp = 'PARSESRC'SpecWord
  228.                   if SpecWord = 9 then
  229.                      LkUp = 'PARSESRC7'
  230.                end
  231.                else
  232.                   LkUp = 'PARSETMP1'
  233.             end
  234.             when Kwd = 'OPTIONS' then
  235.                if find('RESULTS PROMPT FAILAT CASHE ON OFF', upper(LkUp)) > 0 then
  236.                   LkUp = Kwd
  237.             when Kwd = 'TRACE' then
  238.                if verify(upper(right(LkUp,1)), 'IRACLENOBS') = 0 then
  239.                   LkUp = 'TRACEOPT'
  240.             when Kwd = 'SIGNAL' then
  241.                if find('ON OFF', upper(LkUp)) >0 then
  242.                   LkUp = 'SIGTRAP'
  243.                else if pos('BREAK_', upper(LkUp)) = 1 then
  244.                   LkUp = 'BREAK_'
  245.                else
  246.                   LkUp = 'SIGTRAN'
  247.             when upper(LkUp) = 'UPPER' then
  248.                if Kwd ~= 'UPPER' then
  249.                   LkUp = 'UPPER()'
  250.             when Kwd = 'ADDRESS' then
  251.                if abbrev(word(HLine, 2), LkUp) then do
  252.                   LkUp = Kwd
  253.                   leave CheckOpt  /* so we don't get address() */
  254.                end
  255.             otherwise
  256.          end
  257.          if GetXRef(LkUp'()') ~= 10 then
  258.             LkUp = LkUp'()'
  259.       end
  260.    end
  261. end
  262. else do     /* Word was supplied as argument to the script */
  263.    upper LkUp
  264.    CurPos = 1; HLine = LkUp
  265. end
  266.  
  267. XRVal = upper(GetXRef(LkUp))
  268. if find('10 NODE/K', XRVal) > 0 then do
  269.    if ShowInfo( HLine, LkUp, CurPos, Kwd ) = 0 then signal CleanExit
  270.    else XRVal = upper(GetXRef(LkUp))
  271. end
  272.  
  273. if ~abbrev(word(XRVal,2), '"ARX_') then
  274.    Src = 'Other ref:'
  275. else
  276.    Src = 'ARexxGuide:'
  277. call DisplayStatus(Src upper(LkUp) '['word('Explanation Function Instruction', word(getxref(LkUp), 3)+1)']')
  278.  
  279. signal on error
  280. call DisplayAG(LkUp)
  281. signal off error
  282.  
  283. CleanExit:
  284.     /* Since it can cause severe problems, it's removed when not needed */
  285. call remlib('amigaguide.library')
  286. call EditorExit()
  287. exit 0
  288.  
  289. /*************************************************************************
  290. ** Error routines.                                                      */
  291.  
  292. XRefError:
  293.    ErrMsg = 'Unable to load the cross-reference file ARx_Guide.xref\'
  294.    if ~exists('env:amigaguide/path') then
  295.       ErrMsg = ErrMsg || 'Your environmental variable "amigaguide/path" is not set.\'
  296.    else
  297.       ErrMsg = ErrMsg '  Be sure to include that file''s directory in env:amigaguide/path.\'
  298.    ErrMsg = ErrMsg '  The .xref file may be put into the current directory or into\',
  299.    '  any directory included in the amigaguide/path environmental variable.'
  300.    call PutErrMsg(SIGL, ErrMsg)
  301.    exit 6
  302. NoCmd:
  303.    call PutErrMsg(SIGL, 'Can''t read environmental variable "env:arexxguide/AGcmd".\',
  304.              '  That variable must hold the name of the command you use\',
  305.              '  to show AmigaGuide files.')
  306.    exit 7
  307. Syntax:
  308.    call PutErrMsg(SIGL, '   Syntax error #'rc ':' errortext(rc))
  309.    exit 9
  310. Failure:
  311.    call PutErrMsg(SIGL, '   Command "'sourceline(SIGL)'" failed.')
  312.    exit 10
  313. Halt:
  314. Break_C:
  315.    call PutErrMsg(SIGL, '   Execution halted.')
  316.    exit 8
  317. Error:
  318.    call PutErrMsg(SIGL, '   Command error in display routine: RC =' rc'.\\',
  319.              '  The name of your AmigaGuide viewer may not be properly set, or\',
  320.              '  an .xref file might have been loaded for a database not located\',
  321.              '  in one of the directories in env:AmigaGuide/path.\',
  322.              '  Type rx ARx_Setup at a shell prompt to run the setup program again.')
  323.    exit 9
  324. PutErrMsg:
  325.    call trace b
  326.    ErrMsg ='Sorry an enexpected error has occurred in line' arg(1)'.\\'arg(2)
  327.    signal off syntax
  328.    signal off halt
  329.    signal off break_c
  330.    WinHi = 59 + CountChar('\', ErrMsg) * 11
  331.    call DisplayStatus('ARx_Help macro error.')
  332.    if open(6ErrWin, 'raw:0/0/640/'WinHi'/Arx_Help Error/SCREEN *') then do
  333.       call writeln(6ErrWin, translate(ErrMsg,'0a'x, '\'))
  334.       call writech(6ErrWin, '0a'x'        -- Press any key -- ')
  335.       call readch(6ErrWin)
  336.    end
  337.    call EditorExit()
  338.    return 0
  339.  
  340. exit 0
  341.  
  342. /*************************************************************************
  343. ** General utility routines                                             */
  344.  
  345. GetAGCmd: procedure
  346.       /* Calls the setup program if env:ARexxGuide/AGCmd is not set       */
  347.    AGCmd = GetEnv('ARexxGuide/AGCmd')
  348.    if AGCmd = '' then do
  349.       call DisplayStatus('Please wait... Running setup routine')
  350.       if 'ARx_Setup.rexx'('HELP',,address()) ~= 0 then
  351.          signal NoCmd
  352.       else
  353.          AGCmd = GetEnv('ARexxGuide/AGCmd')
  354.    end
  355.    return AGCmd
  356.  
  357. ShowInfo:
  358.       /* The env: var ShowFullHelp determines whether a simple editor     **
  359.       ** requester is used, or a complex rexxarplib window that displays  **
  360.       ** far more information about the current line, but is also slow.   */
  361.    if getenv('ARexxGuide/ShowFullHelp') ~= '1' then do
  362.       if BoolReq(upper(LkUp) 'not found.','Show ARexxGuide index?') then do
  363.          LkUp = 'ARx_NdxCont'
  364.          return 1
  365.       end
  366.       else
  367.          return 0
  368.    end
  369.    else do
  370.          /* Show that we're doing something */
  371.       call DisplayStatus(LkUp 'not found. Checking clause...')
  372.  
  373.          /* The .rexx part of the name has to be added because the default **
  374.          ** extension is set by editor: .TTX, .edge, etc. Since this       **
  375.          ** is set up for different editors, it's stored with the generic  **
  376.          ** extension.                                                     */
  377.       return 'ARx_RArpInfoWin.rexx'(HLine,LkUp,CurPos,Kwd,SetAddress(),SetExecStr(),GetWinInfo())
  378.    end
  379.  
  380. CountChar:
  381.    return length(arg(2)) - length(compress(arg(2), arg(1)))
  382.  
  383. GetEnv: procedure
  384.       /* Arguments:                                                     **
  385.       **   arg(1) := The name of the variable to retrieve               **
  386.       ** Returns     a string                                           */
  387.  
  388.       /* Use external function of same name if it's available           */
  389.    if show('L', 'rexxarplib.library') then
  390.       return 'GetEnv'(arg(1))
  391.       /*  OPEN()  will fail if variable is not defined. Null will be    **
  392.       ** returned in that case                                          */
  393.    if open(6Env, 'env:'arg(1), 'R') then do
  394.       EnvVar = readln(6Env)
  395.       call close 6Env
  396.    end
  397.    else EnvVar = ''
  398.    return EnvVar
  399.  
  400. Tab2Space:  procedure
  401.    parse arg TSize, Line
  402.    tpos=pos('09'x,Line);
  403.    do while tpos>0;
  404.       Line=insert('',Line, tpos, min(1, tpos//TSize)*(TSize-tpos//TSize));
  405.       tpos=pos('09'x, Line,tpos+1);
  406.    end;
  407. return translate(Line,' ','09'x)
  408.  
  409.  
  410. /***********************************************************************
  411. ** Editor-specific commands.                                          **
  412. **    These function calls are used elsewhere to make it easier to    **
  413. **    use the same script for different editors.                      */
  414.  
  415.  
  416. GetCLine: procedure expose EdInfo.
  417.    'rv' '/EdInfo/'
  418.    if rc ~= 0 then signal error
  419.    return EdInfo.Current
  420.  
  421. GetCurPos: procedure expose EdInfo.
  422.    if symbol('EdInfo.X') ~= 'VAR' then do
  423.       'rv' '/EdInfo/'
  424.       if rc ~= 0 then signal error
  425.    end
  426.    return EdInfo.Base + EdInfo.X
  427.  
  428. GetCWord:  procedure
  429.       /* Send back current word */
  430.    parse arg Line, CPos
  431.       /* list of valid word-separator charaters: space, operator, spec char */
  432.    SepChar = ' +-*/%|&~=><^,;:()."''\'
  433.       /* Back up in line if Current position is not an alpha character  or digit */
  434.    do while (verify(substr(Line,CPos,1), SepChar || '0a'x) = 0) & (CPos > 1)
  435.       CPos = CPos - 1
  436.    end
  437.       /* Cut off word at space _or_ any of the operator or special characters     */
  438.    Word = reverse(left(Line,verify(Line' ',SepChar,'M',CPos) - 1))
  439.    return reverse(left(Word,verify(Word' ',SepChar,'M', max(1,length(Word) + 1 - CPos))-1))
  440.  
  441. DisplayStatus:
  442.       /* Put message in title bar of editor */
  443.   'sm' '"'arg(1)'"'
  444.    return 0
  445.  
  446. BoolReq: procedure
  447.       /* Present a Yes/No requester                                      **
  448.       ** Arguments:                                                      **
  449.       **    arg(1)   := Titlebar text                                    **
  450.       **    arg(2)   := Message in body of requester                     */
  451.    if show('L', 'rexxreqtools.library') then
  452.       return rtezrequest(arg(2), '_Yes|_No', arg(1))
  453.    csi='9b'x;bold=csi'1m';norm=csi'0m';black=csi'31m';white=csi'32m';blue=csi'33m'
  454.    if open(6InfoWin, 'raw:50/50/300/78/'arg(1)'/') then do
  455.       call writeln(6InfoWin, csi'302070'x || '0a'x || center(arg(2),58))
  456.       call writeln(6InfoWin, '0a'x blue '      Respond' white||bold'y'norm||blue 'or' white||bold'n'blue||norm'.')
  457.       call writech(6InfoWin, black '  [<'white'Enter'black'> =' white'Y'black'. <'white'Esc'black'> =' white'N'black']')
  458.       resp = readch(6InfoWin)
  459.       call close 6InfoWin
  460.       return (upper(resp) = 'Y' | resp = '0d'x)
  461.    end
  462.  
  463. GetWinInfo:
  464.       /* Return Information about window                                 **
  465.       **    Format of returned value:                                    **
  466.       **           word 1 = x position                                   **
  467.       **           word 2 - y position                                   **
  468.       **           word 3 = name of public screen                        */
  469.    /* Can't get window info for ed.                                      */
  470.    return 0 0 ""
  471.  
  472. EditorExit:
  473.       /* Used for any editor-specific commands that should be issued on  **
  474.       ** exit.                                                           */
  475.    return 0
  476.  
  477. SetExecStr:
  478.       /* The string returned by this function is used in the rexxarplib  **
  479.       ** window to send link commands to AmigaGuide.                     */
  480.    AGCmd = GetEnv('arexxguide/agcmd')
  481.    if AGCmd = '' then do
  482.       NoCmd_Return = 'ReShowInfo'
  483.       signal NoCmd
  484.    end
  485.    if ~abbrev(AGCmd, 'Multi') then
  486.       return 'address ARX_ARP quit; if ~show(P, ARX_GUIDE) then do;address command; ''run >nil:' AGCmd 'ARexxGuide.guide portname ARX_GUIDE'';waitforport ARX_GUIDE; end; address ARX_GUIDE; windowtofront; link'
  487.    else
  488.       return 'address ARX_ARP quit; address command; ''run' AGCmd 'document'
  489.  
  490. SetAddress:
  491.       /* Address used as target of the commands from rarp window         */
  492.    return 'ARX_HELP'
  493.  
  494. DisplayAG:
  495.       /* Show the node in AmigaGuide viewer. Missing env: will call      **
  496.       ** error handler which runs the setup prg. and then passes         **
  497.       ** control back to here.                                           */
  498.    arg LkUp
  499.    AGCmd = GetEnv('arexxguide/agcmd')
  500.    if AGCmd = '' then do
  501.       NoCmd_Return = 'ShowFunc'
  502.       signal NoCmd
  503.    end
  504.  
  505.  
  506.    if abbrev(AGCmd, 'Multi') then
  507.       PrtOpt = ''
  508.    else
  509.       PrtOpt = 'portname ARX_GUIDE'
  510.  
  511.       /* See if our AG window is open */
  512.    if ~show('P','ARX_GUIDE') then do
  513.       if pos('/', LkUp) > 0 then
  514.          parse var LkUp DB '/' Doc
  515.       else
  516.          DB = ''
  517.       address command 'run' AGCmd DB 'document' LkUp PrtOpt 'requester'
  518.       if rc > 5 then do
  519.          NoCmd_Return = 'ShowFunc'
  520.          signal NoCmd
  521.       end
  522.  
  523.    end
  524.    else do
  525.          /* show the node */
  526.       address ARX_GUIDE 'Link' LkUp
  527.       address ARX_GUIDE 'unzoomwindow'
  528.       address ARX_GUIDE 'windowtofront'
  529.          /* This will be called only if rexxarplib is available and will **
  530.          ** try to bring a public screen to the foreground. OK if there  **
  531.          ** isn't a screen of that name available.                       */
  532.       if show('L', 'rexxarplib.library') then
  533.          call screentofront('ARX_GUIDE')
  534.    end
  535. return 0
  536.